//
//  StudioConfiguration.m
//  AnotherMediaTool
//
//  Created by fh on 2018/9/13.
//  Copyright © 2018年 fh. All rights reserved.
//

#import "StudioConfiguration.h"
#import <AVFoundation/AVFoundation.h>
#import <VideoToolbox/VTCompressionProperties.h>

@interface StudioConfiguration ()
/** 私有视频输出配置 */
@property (nonatomic, readwrite, strong) NSMutableDictionary *privateVideoOutputSettings;
/** 私有视频ProfileLevel */
@property (nonatomic, readwrite, strong) NSString *privateRealVideoProfileLevel;
/** 私有会话采集分辨率 */
@property (nonatomic, readwrite, strong) AVCaptureSessionPreset privateRealSessionPreset;
/** 私有输出分辨率 */
@property (nonatomic, readwrite, strong) AVOutputSettingsPreset privateRealOutputSettingsPreset;
/** 私有音频输出配置 */
@property (nonatomic, readwrite, strong) NSMutableDictionary *privateAudioOutputSettings;
/** 私有音频输出格式 */
@property (nonatomic, readwrite, assign) NSInteger privateRealAudioFormatID;
@end

@implementation StudioConfiguration

#pragma mark - Life

/**
 初始化方法

 @return 实例对象
 */
- (instancetype)init {
    if (self = [super init]) {
        [self initDefaultOutputSettings];
    }
    return self;
}

/**
 初始化单例对象
 
 @return 单例对象
 */
+ (instancetype)shareInstanceConfiguration {
    static StudioConfiguration *videoConfiguration = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        videoConfiguration = [StudioConfiguration defaultConfiguration];
    });
    return videoConfiguration;
}

#pragma mark - Private

/**
 初始化默认配置

 @return 实例对象
 */
+ (instancetype)defaultConfiguration {
    return [[StudioConfiguration alloc] init];
}

/**
 初始化默认输出配置:音视频
 */
- (void)initDefaultOutputSettings {
    self.videoConfigurationMode = SCVideoConfigurationModeCustom;
    [self initDefaultAudioOutputSettings];
    [self initDefaultVideoOutputSettings];
}

/**
 初始化默认视频输出配置
 */
- (void)initDefaultVideoOutputSettings {
    self.privateVideoOutputSettings = [NSMutableDictionary dictionary];
    
    self.videoOutputSettingsPreset = SCOutputSettingsPreset1920x1080;
    
    self.videoCodecType = SCVideoCodecTypeH264;
    self.videoWidth = 1080;
    self.videoHeight = 1920;
    self.videoScalingMode = SCVideoScalingModeResizeAspectFill;
    
    self.videoPixelAspectRatio = SCVideoPixelAspectRatioMake(1, 1);
//    self.videoCleanAperture = SCVideoCleanApertureMake(375, 667, 80, 100);
    
    self.videoAverageBitRate = 1500 * 1000;
    self.videoExpectedSourceFrameRate = 30;
    self.videoProfileLevel = SCVideoProfileLevelH264MainAutoLevel;
    
//    self.videoMaxKeyFrameInterval = 1;
//    self.videoMaxKeyFrameIntervalDuration = 0.0;
//    self.videoH264EntropyMode = SCVideoH264EntropyModeCABAC;
//    self.videoAllowFrameReordering = NO;
//    self.videoAverageNonDroppableFrameRate = 30;
    
    self.sessionPreset = SCCaptureSessionPresetPhoto; //  SCCaptureSessionPresetHigh
}

/**
 初始化默认音频输出配置
 */
- (void)initDefaultAudioOutputSettings {
    self.privateAudioOutputSettings = [NSMutableDictionary dictionary];
    self.audioFormatID = SCAudioFormatAAC;
    self.audioNumberOfChannels = 2;
    self.audioSampleRate = 44100;
    self.audioEncoderBitRatePerChannel = 64000;
//    self.audioEncoderAudioQuality = SCAudioEncoderAudioQualityHigh;
//    self.audioLinearPCMBitDepth = 8;
}

/**
 重置默认输出配置
 */
- (void)resetDefaultConfigurate {
    [self initDefaultOutputSettings];
}

/** 比色法: iOS10之后 */
- (NSDictionary *)videoColorPropertiesWithSCVideoColorPropertiesMode:(SCVideoColorPropertiesMode)colorimetryMode {
    NSMutableDictionary *colorDic = [NSMutableDictionary dictionary];
    switch (colorimetryMode) {
        case SCVideoColorPropertiesModeHD: {
            if (@available(iOS 10.0, *)) {
                colorDic[AVVideoColorPrimariesKey] = AVVideoColorPrimaries_ITU_R_709_2;
                colorDic[AVVideoTransferFunctionKey] = AVVideoTransferFunction_ITU_R_709_2;
                colorDic[AVVideoYCbCrMatrixKey] = AVVideoYCbCrMatrix_ITU_R_709_2;
            }
        }
            break;
        case SCVideoColorPropertiesModeSD: {
            if (@available(iOS 10.0, *)) {
                colorDic[AVVideoColorPrimariesKey] = AVVideoColorPrimaries_SMPTE_C;
                colorDic[AVVideoTransferFunctionKey] = AVVideoTransferFunction_ITU_R_709_2;
                colorDic[AVVideoYCbCrMatrixKey] = AVVideoYCbCrMatrix_ITU_R_601_4;
            }
        }
            break;
        case SCVideoColorPropertiesModeWideGamutHD: {
            if (@available(iOS 10.0, *)) {
                colorDic[AVVideoColorPrimariesKey] = AVVideoColorPrimaries_P3_D65;
                colorDic[AVVideoTransferFunctionKey] = AVVideoTransferFunction_ITU_R_709_2;
                colorDic[AVVideoYCbCrMatrixKey] = AVVideoYCbCrMatrix_ITU_R_709_2;
            }
        }
            break;
    }
    return colorDic;
}

/**
 配置视频压缩参数

 @param key 键
 @param value 值
 */
- (void)configVideoCompressionPropertiesDicWithKey:(id)key value:(id)value {
    if (self.privateVideoOutputSettings[AVVideoCompressionPropertiesKey]) {
        self.privateVideoOutputSettings[AVVideoCompressionPropertiesKey][key] = value;
    }else {
        NSMutableDictionary *compressionPropertiesDic = [NSMutableDictionary dictionary];
        compressionPropertiesDic[key] = value;
        self.privateVideoOutputSettings[AVVideoCompressionPropertiesKey] = compressionPropertiesDic;
    }
}

#pragma mark - Setter and getter
#pragma mark - video
- (NSMutableDictionary *)videoOutputSettings {
    if (self.videoConfigurationMode == SCVideoConfigurationModeCustom) {
        self.privateVideoOutputSettings[AVVideoWidthKey] = @(self.videoWidth);
        self.privateVideoOutputSettings[AVVideoHeightKey] = @(self.videoHeight);
    
        if (self.privateVideoOutputSettings[AVVideoCompressionPropertiesKey]) {
            self.privateVideoOutputSettings[AVVideoCompressionPropertiesKey][AVVideoAverageBitRateKey] = @(self.videoAverageBitRate);
            self.privateVideoOutputSettings[AVVideoCompressionPropertiesKey][AVVideoExpectedSourceFrameRateKey] = @(self.videoExpectedSourceFrameRate);
            self.privateVideoOutputSettings[AVVideoCompressionPropertiesKey][AVVideoProfileLevelKey] = self.privateRealVideoProfileLevel;
        }else {
            NSMutableDictionary *compressionPropertiesDic = [NSMutableDictionary dictionary];
            compressionPropertiesDic[AVVideoAverageBitRateKey] = @(self.videoAverageBitRate);
            compressionPropertiesDic[AVVideoExpectedSourceFrameRateKey] = @(self.videoExpectedSourceFrameRate);
            compressionPropertiesDic[AVVideoProfileLevelKey] = self.privateRealVideoProfileLevel;
            self.privateVideoOutputSettings[AVVideoCompressionPropertiesKey] = compressionPropertiesDic;
        }
        return self.privateVideoOutputSettings;
    }else {
        AVOutputSettingsAssistant *outputSettingsAssistant = [AVOutputSettingsAssistant outputSettingsAssistantWithPreset:self.privateRealOutputSettingsPreset];
        return [NSMutableDictionary dictionaryWithDictionary:outputSettingsAssistant.videoSettings];
    }
}
- (void)setVideoPixelAspectRatio:(SCVideoPixelAspectRatio)videoPixelAspectRatio {
    _videoPixelAspectRatio = videoPixelAspectRatio;
    self.privateVideoOutputSettings[AVVideoPixelAspectRatioKey] = @{AVVideoPixelAspectRatioHorizontalSpacingKey : @(videoPixelAspectRatio.horizontalSpacing <= 0 ? 1 : videoPixelAspectRatio.horizontalSpacing),
                                                                    AVVideoPixelAspectRatioVerticalSpacingKey : @(videoPixelAspectRatio.verticalSpacing <= 0 ? 1 : videoPixelAspectRatio.verticalSpacing)
                                                                    }; // 居中按比例截
}
- (void)setVideoCleanAperture:(SCVideoCleanAperture)videoCleanAperture {
    _videoCleanAperture = videoCleanAperture;
    self.privateVideoOutputSettings[AVVideoCleanApertureKey] = @{
                                                                 AVVideoCleanApertureWidthKey : @(videoCleanAperture.width),
                                                                 AVVideoCleanApertureHeightKey : @(videoCleanAperture.height),
                                                                 AVVideoCleanApertureHorizontalOffsetKey : @(videoCleanAperture.horizontalOffset),
                                                                 AVVideoCleanApertureVerticalOffsetKey : @(videoCleanAperture.verticalOffset)
                                                                 };
}
- (void)setVideoMaxKeyFrameInterval:(NSUInteger)videoMaxKeyFrameInterval {
    _videoMaxKeyFrameInterval = videoMaxKeyFrameInterval;
    [self configVideoCompressionPropertiesDicWithKey:AVVideoMaxKeyFrameIntervalKey value:@(videoMaxKeyFrameInterval)];
}
- (void)setVideoMaxKeyFrameIntervalDuration:(float)videoMaxKeyFrameIntervalDuration {
    _videoMaxKeyFrameIntervalDuration = videoMaxKeyFrameIntervalDuration;
    [self configVideoCompressionPropertiesDicWithKey:AVVideoMaxKeyFrameIntervalDurationKey value:@(videoMaxKeyFrameIntervalDuration)];
}
- (void)setVideoAllowFrameReordering:(BOOL)videoAllowFrameReordering {
    _videoAllowFrameReordering = videoAllowFrameReordering;
    [self configVideoCompressionPropertiesDicWithKey:AVVideoAllowFrameReorderingKey value:@(videoAllowFrameReordering)];
}
- (void)setVideoAverageNonDroppableFrameRate:(NSUInteger)videoAverageNonDroppableFrameRate {
    _videoAverageNonDroppableFrameRate = videoAverageNonDroppableFrameRate;
    [self configVideoCompressionPropertiesDicWithKey:AVVideoAverageNonDroppableFrameRateKey value:@(videoAverageNonDroppableFrameRate)];
}
- (void)setVideoScalingMode:(SCVideoScalingMode)videoScalingMode {
    _videoScalingMode = videoScalingMode;
    switch (videoScalingMode) {
        case SCVideoScalingModeFit:
            self.privateVideoOutputSettings[AVVideoScalingModeKey] = AVVideoScalingModeFit;
            break;
        case SCVideoScalingModeResize:
            self.privateVideoOutputSettings[AVVideoScalingModeKey] = AVVideoScalingModeResize;
            break;
        case SCVideoScalingModeResizeAspect:
            self.privateVideoOutputSettings[AVVideoScalingModeKey] = AVVideoScalingModeResizeAspect;
            break;
        case SCVideoScalingModeResizeAspectFill:
            self.privateVideoOutputSettings[AVVideoScalingModeKey] = AVVideoScalingModeResizeAspectFill;
            break;
    }
}
- (void)setVideoCodecType:(SCVideoCodecType)videoCodecType {
    _videoCodecType = videoCodecType;
    switch (videoCodecType) {
        case SCVideoCodecTypeH264: {
            if (@available(iOS 11.0, *)) {
                self.privateVideoOutputSettings[AVVideoCodecKey] = AVVideoCodecTypeH264;
            } else {
                self.privateVideoOutputSettings[AVVideoCodecKey] = AVVideoCodecH264;
            }
        }
            break;
        case SCVideoCodecTypeJPEG: {
            if (@available(iOS 11.0, *)) {
                self.privateVideoOutputSettings[AVVideoCodecKey] = AVVideoCodecTypeJPEG;
            } else {
                self.privateVideoOutputSettings[AVVideoCodecKey] = AVVideoCodecJPEG;
            }
        }
            break;
    }
}
- (void)setVideoProfileLevel:(SCVideoProfileLevel)videoProfileLevel {
    _videoProfileLevel = videoProfileLevel;
    switch (videoProfileLevel) {
        case SCVideoProfileLevelH264Baseline30:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264Baseline30;
            break;
        case SCVideoProfileLevelH264Baseline31:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264Baseline31;
            break;
        case SCVideoProfileLevelH264Baseline41:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264Baseline41;
            break;
        case SCVideoProfileLevelH264BaselineAutoLevel:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264BaselineAutoLevel;
            break;
        case SCVideoProfileLevelH264Main30:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264Main30;
            break;
        case SCVideoProfileLevelH264Main31:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264Main31;
            break;
        case SCVideoProfileLevelH264Main32:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264Main32;
            break;
        case SCVideoProfileLevelH264Main41:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264Main41;
            break;
        case SCVideoProfileLevelH264MainAutoLevel:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264MainAutoLevel;
            break;
        case SCVideoProfileLevelH264High40:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264High40;
            break;
        case SCVideoProfileLevelH264High41:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264High41;
            break;
        case SCVideoProfileLevelH264HighAutoLevel:
            self.privateRealVideoProfileLevel = AVVideoProfileLevelH264HighAutoLevel;
            break;
    }
}
- (void)setVideoH264EntropyMode:(SCVideoH264EntropyMode)videoH264EntropyMode {
    _videoH264EntropyMode = videoH264EntropyMode;
    switch (videoH264EntropyMode) {
        case SCVideoH264EntropyModeCABAC:
            [self configVideoCompressionPropertiesDicWithKey:AVVideoH264EntropyModeKey value:AVVideoH264EntropyModeCABAC];
            break;
        case SCVideoH264EntropyModeCAVLC:
            [self configVideoCompressionPropertiesDicWithKey:AVVideoH264EntropyModeKey value:AVVideoH264EntropyModeCAVLC];
            break;
    }
}
- (void)setSessionPreset:(SCCaptureSessionPreset)sessionPreset {
    _sessionPreset = sessionPreset;
    switch (sessionPreset) {
        case SCCaptureSessionPresetPhoto:
            self.privateRealSessionPreset = AVCaptureSessionPresetPhoto;
            break;
        case SCCaptureSessionPresetHigh:
            self.privateRealSessionPreset = AVCaptureSessionPresetHigh;
            break;
        case SCCaptureSessionPresetMedium:
            self.privateRealSessionPreset = AVCaptureSessionPresetMedium;
            break;
        case SCCaptureSessionPresetLow:
            self.privateRealSessionPreset = AVCaptureSessionPresetLow;
            break;
        case SCCaptureSessionPreset352x288:
            self.privateRealSessionPreset = AVCaptureSessionPreset352x288;
            break;
        case SCCaptureSessionPreset640x480:
            self.privateRealSessionPreset = AVCaptureSessionPreset640x480;
            break;
        case SCCaptureSessionPreset1280x720:
            self.privateRealSessionPreset = AVCaptureSessionPreset1280x720;
            break;
        case SCCaptureSessionPreset1920x1080:
            self.privateRealSessionPreset = AVCaptureSessionPreset1920x1080;
            break;
        case SCCaptureSessionPreset3840x2160:
            self.privateRealSessionPreset = AVCaptureSessionPreset3840x2160;
            break;
        case SCCaptureSessionPresetiFrame960x540:
            self.privateRealSessionPreset = AVCaptureSessionPresetiFrame960x540;
            break;
        case SCCaptureSessionPresetiFrame1280x720:
            self.privateRealSessionPreset = AVCaptureSessionPresetiFrame1280x720;
            break;
        case SCCaptureSessionPresetInputPriority:
            self.privateRealSessionPreset = AVCaptureSessionPresetInputPriority;
            break;
    }
}
- (void)setVideoOutputSettingsPreset:(SCOutputSettingsPreset)videoOutputSettingsPreset {
    _videoOutputSettingsPreset = videoOutputSettingsPreset;
    switch (videoOutputSettingsPreset) {
        case SCOutputSettingsPreset640x480:
            self.privateRealOutputSettingsPreset = AVOutputSettingsPreset640x480;
            break;
        case SCOutputSettingsPreset960x540:
            self.privateRealOutputSettingsPreset = AVOutputSettingsPreset960x540;
            break;
        case SCOutputSettingsPreset1280x720:
            self.privateRealOutputSettingsPreset = AVOutputSettingsPreset1280x720;
            break;
        case SCOutputSettingsPreset1920x1080:
            self.privateRealOutputSettingsPreset = AVOutputSettingsPreset1920x1080;
            break;
        case SCOutputSettingsPreset3840x2160:
            self.privateRealOutputSettingsPreset = AVOutputSettingsPreset3840x2160;
            break;
        case SCOutputSettingsPresetHEVC1920x1080:
            if (@available(iOS 11.0, *)) {
                self.privateRealOutputSettingsPreset = AVOutputSettingsPresetHEVC1920x1080;
            } else {
                self.privateRealOutputSettingsPreset = AVOutputSettingsPreset3840x2160;
            }
            break;
        case SCOutputSettingsPresetHEVC3840x2160:
            if (@available(iOS 11.0, *)) {
                self.privateRealOutputSettingsPreset = AVOutputSettingsPresetHEVC3840x2160;
            } else {
                self.privateRealOutputSettingsPreset = AVOutputSettingsPreset3840x2160;
            }
            break;
    }
}
- (NSString *)avCaptureSessionPreset {
    return self.privateRealSessionPreset;
}
#pragma mark - audio
- (NSMutableDictionary *)audioOutputSettings {
    if (self.videoConfigurationMode == SCVideoConfigurationModeCustom) {
        self.privateAudioOutputSettings[AVFormatIDKey] = @(self.privateRealAudioFormatID);
        if (self.audioNumberOfChannels > 2 || self.audioNumberOfChannels < 1) {
            self.audioNumberOfChannels = 2;
        }
        self.privateAudioOutputSettings[AVNumberOfChannelsKey] = @(self.audioNumberOfChannels);
        self.privateAudioOutputSettings[AVSampleRateKey] = @(self.audioSampleRate);
        self.privateAudioOutputSettings[AVChannelLayoutKey] = self.audioChannelLayout;
        return self.privateAudioOutputSettings;
    }else {
        AVOutputSettingsAssistant *outputSettingsAssistant = [AVOutputSettingsAssistant outputSettingsAssistantWithPreset:self.privateRealOutputSettingsPreset];
        return [NSMutableDictionary dictionaryWithDictionary:outputSettingsAssistant.audioSettings];
    }
}
- (void)setAudioEncoderBitRatePerChannel:(NSInteger)audioEncoderBitRatePerChannel {
    _audioEncoderBitRatePerChannel = audioEncoderBitRatePerChannel;
    self.privateAudioOutputSettings[AVEncoderBitRatePerChannelKey] = @(audioEncoderBitRatePerChannel);
}
- (void)setAudioLinearPCMBitDepth:(NSInteger)audioLinearPCMBitDepth {
    _audioLinearPCMBitDepth = audioLinearPCMBitDepth;
    self.privateAudioOutputSettings[AVLinearPCMBitDepthKey] = @(audioLinearPCMBitDepth);
}

- (void)setAudioFormatID:(SCAudioFormat)audioFormatID {
    _audioFormatID = audioFormatID;
    switch (audioFormatID) {
        case SCAudioFormatAAC:
            self.privateRealAudioFormatID = kAudioFormatMPEG4AAC;
            break;
        case SCAudioFormatMP3:
            self.privateRealAudioFormatID = kAudioFormatMPEGLayer3;
            break;
    }
}
- (void)setAudioEncoderAudioQuality:(SCAudioEncoderAudioQuality)audioEncoderAudioQuality {
    _audioEncoderAudioQuality = audioEncoderAudioQuality;
    switch (audioEncoderAudioQuality) {
        case SCAudioEncoderAudioQualityMin:
            self.privateAudioOutputSettings[AVEncoderAudioQualityKey] = @(AVAudioQualityMin);
            break;
        case SCAudioEncoderAudioQualityLow:
            self.privateAudioOutputSettings[AVEncoderAudioQualityKey] = @(AVAudioQualityLow);
            break;
        case SCAudioEncoderAudioQualityMedium:
            self.privateAudioOutputSettings[AVEncoderAudioQualityKey] = @(AVAudioQualityMedium);
            break;
        case SCAudioEncoderAudioQualityHigh:
            self.privateAudioOutputSettings[AVEncoderAudioQualityKey] = @(AVAudioQualityHigh);
            break;
        case SCAudioEncoderAudioQualityMax:
            self.privateAudioOutputSettings[AVEncoderAudioQualityKey] = @(AVAudioQualityMax);
            break;
    }
}

@end







